﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Scripting.Hosting;
using IronPython.Hosting;
using Microsoft.Scripting;
using Microsoft.Xna.Framework;

namespace SunrayEngine.Managers
{
    public class PythonManager
    {
        #region fields
        private ScriptEngine engine;
        private ScriptScope scope;
        #endregion

        #region properties
        public dynamic this[string key]
        {
            get
            {
                if (!scope.ContainsVariable(key))
                    throw new Exception("Unassigned variable exception.");

                return scope.GetVariable(key);
            }

            set
            {
                scope.SetVariable(key, value);
            }
        }

        public string Name
        { get; private set; }

        public string[] Vars
        {
            get { return scope.GetVariableNames().ToArray(); }
        }
        #endregion

        #region initialization
        public PythonManager(Game game)
        {
            engine = Python.CreateEngine();
            scope = engine.CreateScope();

            scope.SetVariable("game", game);

            Execute("import inspect");
        }

        public PythonManager(Game game, string filePath)
            :this(game)
        {
            ExecuteFile(filePath);
        }

        public PythonManager(Game game, params string[] filePaths)
            :this(game)
        {
            for (int i = 0; i < filePaths.Length; i++)
            {
                ScriptSource source = engine.CreateScriptSourceFromFile(filePaths[i]);
                execute(source);
            }
        }
        #endregion

        #region methods

        public string GetSource(string key)
        {
            return Execute(string.Format(@"
inspect.getsource({0}) if inspect.isfunction({0}) or inspect.isclass({0}) or inspect.ismethod({0}) \
else inspect.getsource(type({0}))", key));
        }

        #region execution
        public dynamic Execute(string code)
        {
            ScriptSource source = engine.CreateScriptSourceFromString(code);
            return execute(source, string.Format("source:\n{0}", code));
        }

        public dynamic Execute(string code, SourceCodeKind kind)
        {
            ScriptSource source = engine.CreateScriptSourceFromString(code, kind);
            return execute(source, string.Format("source:\n{0}", code));
        }

        public dynamic ExecuteFile(string path)
        {
            ScriptSource source = engine.CreateScriptSourceFromFile(path);
            return execute(source, string.Format("path: {0}\n\nsource:\n{1}", path, source.GetCode()));
        }

        public dynamic ExecuteFile(string path, SourceCodeKind kind)
        {
            ScriptSource source = engine.CreateScriptSourceFromFile(path, Encoding.Default, kind);
            return execute(source, string.Format("path: {0}\n\nsource:\n{1}", path, source.GetCode()));
        }

        private dynamic execute(ScriptSource source, string exceptionInfo = "")
        {
            try
            {
                return source.Execute(scope);
            }
            catch(Exception e)
            {
                PopBox.Show("Python Error", "There was an error when executing the script:\n\n" + exceptionInfo + (exceptionInfo != "" ? "\n\n" : "") + "info:\n" + e);
            }

            return null;
        }
        #endregion

        /// <summary>
        /// Returns true there is an value assigned to the key.
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public bool Exists(string key)
        {
            return scope.ContainsVariable(key);
        }
        #endregion
    }
}
